package xin.qixia.robot.ding.client;

import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
import com.aliyun.dingtalkrobot_1_0.models.OrgGroupSendHeaders;
import com.aliyun.dingtalkrobot_1_0.models.OrgGroupSendRequest;
import com.aliyun.dingtalkrobot_1_0.models.OrgGroupSendResponse;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.dingtalk.open.app.api.OpenDingTalkClient;
import com.dingtalk.open.app.api.OpenDingTalkStreamClientBuilder;
import com.dingtalk.open.app.api.callback.OpenDingTalkCallbackListener;
import com.dingtalk.open.app.api.security.AuthClientCredential;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import shade.com.alibaba.fastjson2.JSONObject;
import xin.qixia.robot.core.RobotMessageDispatcher;
import xin.qixia.robot.core.client.RobotClient;
import xin.qixia.robot.core.domain.Robot;
import xin.qixia.robot.core.domain.RobotMessage;
import xin.qixia.robot.ding.convert.DingTalkMessageConvert;
import xin.qixia.robot.ding.domain.DingTalkMessage;
import xin.qixia.robot.ding.domain.DingTalkToken;

import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 钉钉机器人
 */
@RequiredArgsConstructor
@Slf4j
public class DingTalkClient implements RobotClient {

    private final RobotMessageDispatcher dispatcher;

    private final DingTalkMessageConvert dingTalkMessageConvert;

    /**
     * 客户端集合
     */
    private static final Map<String, OpenDingTalkClient> clientMap = new ConcurrentHashMap<>();

    /**
     * 每个账号对应的Token
     */
    private static final Map<String, DingTalkToken> tokenMap = new ConcurrentHashMap<>();

    /**
     * 每个账号对应的机器人信息
     */
    private static final Map<String, Robot> robotMap = new ConcurrentHashMap<>();

    /**
     * 注册机器人
     *
     * @param robot
     * @return
     */
    @Override
    public Robot register(Robot robot) {
        if (clientMap.containsKey(robot.getAccount())) {
            throw new RuntimeException("该机器人账号已存在");
        }

        OpenDingTalkClient client = OpenDingTalkStreamClientBuilder
                .custom()
                .credential(new AuthClientCredential(robot.getClientId(), robot.getClientSecret()))
                .registerCallbackListener("/v1.0/im/bot/messages/get",
                        (OpenDingTalkCallbackListener<DingTalkMessage, JSONObject>) request -> {
                            request.setRobotAccount(robot.getAccount());
                            request.setRobotName(robot.getName());
                            dispatcher.dispatch(request, dingTalkMessageConvert);
                            return new JSONObject();
                        })
                .build();
        clientMap.put(robot.getAccount(), client);
        robotMap.put(robot.getAccount(), robot);
        try {
            client.start();
            return robot;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 注销机器人
     *
     * @param account
     * @return
     */
    @Override
    public Boolean cancel(String account) {
        try {
            clientMap.get(account).stop();
            clientMap.remove(account);
            robotMap.remove(account);
            tokenMap.remove(account);
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取客户端类型
     *
     * @return
     */
    @Override
    public String getType() {
        return "DingTalkRobot";
    }

    /**
     * 发送消息
     *
     * @param message
     * @return
     */
    @Override
    public Boolean send(RobotMessage message) {
        if (!clientMap.containsKey(message.getAccount())) {
            throw new RuntimeException("该机器人账号不存在");
        }

        Robot robot = robotMap.get(message.getAccount());

        OrgGroupSendHeaders orgGroupSendHeaders = new OrgGroupSendHeaders();
        orgGroupSendHeaders.setXAcsDingtalkAccessToken(
                getToken(robot.getAccount(), robot.getClientId(), robot.getClientSecret()));

        OrgGroupSendRequest orgGroupSendRequest = new OrgGroupSendRequest();
        orgGroupSendRequest.setMsgKey("sampleText");
        orgGroupSendRequest.setRobotCode(robot.getAccount());

        orgGroupSendRequest.setOpenConversationId(message.getGroupId());

        JSONObject msgParam = new JSONObject();
        msgParam.put("content", message.getContent());
        orgGroupSendRequest.setMsgParam(msgParam.toJSONString());

        try {
            Config config = new Config();
            config.protocol = "https";
            config.regionId = "central";
            com.aliyun.dingtalkrobot_1_0.Client aclient =
                    new com.aliyun.dingtalkrobot_1_0.Client(config);
            OrgGroupSendResponse orgGroupSendResponse =
                    aclient.orgGroupSendWithOptions(orgGroupSendRequest,
                            orgGroupSendHeaders, new com.aliyun.teautil.models.RuntimeOptions());
            if (Objects.isNull(orgGroupSendResponse) || Objects.isNull(orgGroupSendResponse.getBody())) {
                log.error("RobotGroupMessagesService_send orgGroupSendWithOptions return error, " +
                                "response={}",
                        orgGroupSendResponse);
                return false;
            }
            return true;
        } catch (TeaException e) {
            log.error("RobotGroupMessagesService_send orgGroupSendWithOptions throw TeaException," +
                    " errCode={}, " +
                    "errorMessage={}", e.getCode(), e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error("RobotGroupMessagesService_send orgGroupSendWithOptions throw Exception", e);
            try {
                throw e;
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    /**
     * 获取Token
     *
     * @param account      机器人编码
     * @param clientId     客户端ID
     * @param clientSecret 客户端密钥
     * @return
     */
    private static String getToken(String account, String clientId, String clientSecret) {
        if (tokenMap.containsKey(account) &&
                // 如果过期时间大于当前时间，说明Token没有过期，直接返回
                tokenMap.get(account).getExpireTime() > System.currentTimeMillis()) {
            return tokenMap.get(account).getAccessToken();
        }
        GetAccessTokenRequest getAccessTokenRequest = new GetAccessTokenRequest();
        getAccessTokenRequest.setAppKey(clientId);
        getAccessTokenRequest.setAppSecret(clientSecret);
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        try {
            com.aliyun.dingtalkoauth2_1_0.Client client = new com.aliyun.dingtalkoauth2_1_0.Client(config);
            GetAccessTokenResponse accessToken = client.getAccessToken(getAccessTokenRequest);
            String token = accessToken.getBody().getAccessToken();
            tokenMap.put(account, new DingTalkToken(token,
                    new Date().getTime() + accessToken.getBody().getExpireIn() * 1000));
            return token;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}